<script setup lang="ts">
import type { CronValueObject } from './types';

import { computed, onMounted, reactive, ref, useTemplateRef, watch } from 'vue';

import CrontabDay from './day.vue';
import CrontabHour from './hour.vue';
import CrontabMin from './min.vue';
import CrontabMonth from './month.vue';
import CrontabResult from './result.vue';
import CrontabSecond from './second.vue';
import CrontabWeek from './week.vue';
import CrontabYear from './year.vue';

const props = defineProps<{
    hideComponent?: string[];
}>();

const tabTitles = ['秒', '分钟', '小时', '日', '月', '周', '年'];
const visible = ref(false);
const expression = defineModel<string>();
const cronValueObj = reactive<CronValueObject>({
    second: '*',
    min: '*',
    hour: '*',
    day: '*',
    month: '*',
    week: '?',
    year: '',
});

// refs for child components
const secondCronRef = useTemplateRef<typeof CrontabSecond>('secondCronRef');
const minCronRef = useTemplateRef<typeof CrontabMin>('minCronRef');
const hourCronRef = useTemplateRef<typeof CrontabHour>('hourCronRef');
const dayCronRef = useTemplateRef<typeof CrontabDay>('dayCronRef');
const monthCronRef = useTemplateRef<typeof CrontabMonth>('monthCronRef');
const weekCronRef = useTemplateRef<typeof CrontabWeek>('weekCronRef');
const yearCronRef = useTemplateRef<typeof CrontabYear>('yearCronRef');

const shouldHide = (key: string) => {
    return !(props.hideComponent && props.hideComponent.includes(key));
};

const resolveExp = () => {
    // 反解析 表达式
    if (expression.value) {
        const arr = expression.value.split(' ');
        if (arr.length >= 6) {
            // 6位以上是合法表达式
            const obj: CronValueObject = {
                second: arr[0] ?? '*',
                min: arr[1] ?? '*',
                hour: arr[2] ?? '*',
                day: arr[3] ?? '*',
                month: arr[4] ?? '*',
                week: arr[5] ?? '?',
                year: arr[6] ?? '',
            };
            Object.assign(cronValueObj, obj);
            for (const i in obj) {
                if (obj[i as keyof CronValueObject]) {
                    changeRadio(i, obj[i as keyof CronValueObject]);
                }
            }
        }
    } else {
        // 没有传入的表达式 则还原
        clearCron();
    }
};

// 由子组件触发，更改表达式组成的字段值
const updateCronValue = (name: keyof CronValueObject, value: string, from?: string) => {
    cronValueObj[name] = value;
    if (from && from !== name) {
        changeRadio(name, value);
    }
};

// 赋值到组件
const changeRadio = (name: string, value: string) => {
    const arr = ['second', 'min', 'hour', 'month'];
    const refName = `${name}CronRef`;
    let insValue: number = 1;
    // 获取对应的组件引用
    const componentRefs = {
        secondCronRef,
        minCronRef,
        hourCronRef,
        monthCronRef,
        dayCronRef,
        weekCronRef,
        yearCronRef,
    };
    const componentRef = componentRefs[refName as keyof typeof componentRefs];
    if (!componentRef || !componentRef.value) return;
    const component = componentRef.value;
    if (arr.includes(name)) {
        if (value === '*') {
            insValue = 1;
        } else if (value.includes('-')) {
            const indexArr = value.split('-');
            (component as any).cycle01 = Number.isNaN(Number(indexArr[0])) ? 0 : indexArr[0];
            (component as any).cycle02 = indexArr[1];
            insValue = 2;
        } else if (value.includes('/')) {
            const indexArr = value.split('/');
            (component as any).average01 = Number.isNaN(Number(indexArr[0])) ? 0 : indexArr[0];
            (component as any).average02 = indexArr[1];
            insValue = 3;
        } else {
            insValue = 4;
            (component as any).checkboxList = value.split(',');
        }
    } else {
        switch (name) {
            case 'day': {
                if (value === '*') {
                    insValue = 1;
                } else if (value === '?') {
                    insValue = 2;
                } else if (value.includes('-')) {
                    const indexArr = value.split('-');
                    (component as any).cycle01 = Number.isNaN(Number(indexArr[0])) ? 0 : indexArr[0];
                    (component as any).cycle02 = indexArr[1];
                    insValue = 3;
                } else if (value.includes('/')) {
                    const indexArr = value.split('/');
                    (component as any).average01 = Number.isNaN(Number(indexArr[0])) ? 0 : indexArr[0];
                    (component as any).average02 = indexArr[1];
                    insValue = 4;
                } else if (value.includes('W')) {
                    const indexArr = value.split('W');
                    (component as any).workday = Number.isNaN(Number(indexArr[0])) ? 0 : indexArr[0];
                    insValue = 5;
                } else if (value === 'L') {
                    insValue = 6;
                } else {
                    (component as any).checkboxList = value.split(',');
                    insValue = 7;
                }
                break;
            }
            case 'week': {
                if (value === '*') {
                    insValue = 1;
                } else if (value === '?') {
                    insValue = 2;
                } else if (value.includes('-')) {
                    const indexArr = value.split('-');
                    (component as any).cycle01 = Number.isNaN(Number(indexArr[0])) ? 0 : indexArr[0];
                    (component as any).cycle02 = indexArr[1];
                    insValue = 3;
                } else if (value.includes('#')) {
                    const indexArr = value.split('#');
                    (component as any).average01 = Number.isNaN(Number(indexArr[0])) ? 1 : indexArr[0];
                    (component as any).average02 = indexArr[1];
                    insValue = 4;
                } else if (value.includes('L')) {
                    const indexArr = value.split('L');
                    (component as any).weekday = Number.isNaN(Number(indexArr[0])) ? 1 : indexArr[0];
                    insValue = 5;
                } else {
                    (component as any).checkboxList = value.split(',');
                    insValue = 6;
                }
                break;
            }
            case 'year': {
                if (value === '') {
                    insValue = 1;
                } else if (value === '*') {
                    insValue = 2;
                } else if (value.includes('-')) {
                    insValue = 3;
                } else if (value.includes('/')) {
                    insValue = 4;
                } else {
                    (component as any).checkboxList = value.split(',');
                    insValue = 5;
                }
                break;
            }
            // No default
        }
    }
    (component as any).radioValue = insValue;
};

// 表单选项的子组件校验数字格式（通过-props传递）
const checkNumber = (value: number, minLimit: number, maxLimit: number) => {
    // 检查必须为整数
    value = Math.floor(value);
    if (value < minLimit) {
        value = minLimit;
    } else if (value > maxLimit) {
        value = maxLimit;
    }
    return value;
};

// 隐藏弹窗
const hidePopup = () => {
    visible.value = false;
};

// 填充表达式
const submitFill = () => {
    expression.value = cronValue.value;
    hidePopup();
};

const clearCron = () => {
    // 还原选择项
    Object.assign(cronValueObj, {
        second: '*',
        min: '*',
        hour: '*',
        day: '*',
        month: '*',
        week: '?',
        year: '',
    });
    for (const j in cronValueObj) {
        changeRadio(j, cronValueObj[j as keyof CronValueObject]);
    }
};

const cronValue = computed(() => {
    return `${cronValueObj.second} ${cronValueObj.min} ${cronValueObj.hour} ${cronValueObj.day} ${cronValueObj.month} ${cronValueObj.week}${cronValueObj.year === '' ? '' : ` ${cronValueObj.year}`}`;
});

watch(
    () => expression.value,
    () => {
        resolveExp();
    },
);

onMounted(() => {
    resolveExp();
});
</script>

<template>
    <div style="width: 100%">
        <el-input v-model="expression" placeholder="请输入Cron表达式" clearable>
            <template #append>
                <el-button type="primary" @click="visible = true"> 生成表达式 </el-button>
            </template>
        </el-input>

        <el-dialog title="Cron表达式生成器" v-model="visible" append-to-body destroy-on-close>
            <el-tabs type="border-card">
                <el-tab-pane label="秒" v-if="shouldHide('second')">
                    <CrontabSecond @change="updateCronValue" :check="checkNumber" :cron="cronValueObj" ref="secondCronRef" />
                </el-tab-pane>

                <el-tab-pane label="分钟" v-if="shouldHide('min')">
                    <CrontabMin @change="updateCronValue" :check="checkNumber" :cron="cronValueObj" ref="minCronRef" />
                </el-tab-pane>

                <el-tab-pane label="小时" v-if="shouldHide('hour')">
                    <CrontabHour @change="updateCronValue" :check="checkNumber" :cron="cronValueObj" ref="hourCronRef" />
                </el-tab-pane>

                <el-tab-pane label="日" v-if="shouldHide('day')">
                    <CrontabDay @change="updateCronValue" :check="checkNumber" :cron="cronValueObj" ref="dayCronRef" />
                </el-tab-pane>

                <el-tab-pane label="月" v-if="shouldHide('month')">
                    <CrontabMonth @change="updateCronValue" :check="checkNumber" :cron="cronValueObj" ref="monthCronRef" />
                </el-tab-pane>

                <el-tab-pane label="周" v-if="shouldHide('week')">
                    <CrontabWeek @change="updateCronValue" :check="checkNumber" :cron="cronValueObj" ref="weekCronRef" />
                </el-tab-pane>

                <el-tab-pane label="年" v-if="shouldHide('year')">
                    <CrontabYear @change="updateCronValue" :check="checkNumber" :cron="cronValueObj" ref="yearCronRef" />
                </el-tab-pane>
            </el-tabs>

            <div class="popup-main">
                <div class="popup-result">
                    <p class="title">时间表达式</p>
                    <table>
                        <thead>
                            <tr>
                                <th v-for="item of tabTitles" style="width: 80px" :key="item">{{ item }}</th>
                                <th>Cron 表达式</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr>
                                <td>
                                    <span>{{ cronValueObj.second }}</span>
                                </td>
                                <td>
                                    <span>{{ cronValueObj.min }}</span>
                                </td>
                                <td>
                                    <span>{{ cronValueObj.hour }}</span>
                                </td>
                                <td>
                                    <span>{{ cronValueObj.day }}</span>
                                </td>
                                <td>
                                    <span>{{ cronValueObj.month }}</span>
                                </td>
                                <td>
                                    <span>{{ cronValueObj.week }}</span>
                                </td>
                                <td>
                                    <span>{{ cronValueObj.year }}</span>
                                </td>
                                <td>
                                    <span>{{ cronValue }}</span>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </div>
                <CrontabResult :ex="cronValue" />

                <div class="pop_btn">
                    <el-button type="primary" @click="submitFill">确定</el-button>
                    <el-button type="warning" @click="clearCron">重置</el-button>
                    <el-button type="danger" @click="hidePopup">取消</el-button>
                </div>
            </div>
        </el-dialog>
    </div>
</template>

<style scoped>
.pop_btn {
    text-align: center;
    margin-top: 20px;
}
.popup-main {
    position: relative;
    margin: 10px auto;
    background: #fff;
    border-radius: 5px;
    font-size: 12px;
    overflow: hidden;
}
.popup-title {
    overflow: hidden;
    line-height: 34px;
    padding-top: 6px;
    background: #f2f2f2;
}
.popup-result {
    box-sizing: border-box;
    line-height: 24px;
    margin: 25px auto;
    padding: 15px 10px 10px;
    border: 1px solid #ccc;
    position: relative;
}
.popup-result .title {
    position: absolute;
    top: -15px;
    left: 50%;
    width: 140px;
    font-size: 14px;
    margin-left: -70px;
    text-align: center;
    line-height: 30px;
    background: #fff;
}
.popup-result table {
    text-align: center;
    width: 100%;
    margin: 0 auto;
}
.popup-result table span {
    display: block;
    width: 100%;
    font-family: arial, serif;
    line-height: 30px;
    height: 30px;
    white-space: nowrap;
    overflow: hidden;
    border: 1px solid #e8e8e8;
}
.popup-result-scroll {
    font-size: 12px;
    line-height: 24px;
    height: 10em;
    overflow-y: auto;
}
</style>
